package ace.oauth2.provider.base.autoconfig;

import ace.fw.json.jackson.ObjectMapperFactory;
import ace.oauth2.provider.base.support.DefaultClientDetailsService;
import ace.oauth2.provider.base.support.DefaultTokenEnhancer;
import ace.oauth2.provider.common.converter.ClientDetailsConverter;
import ace.oauth2.provider.common.converter.ClientDetailsConverterImpl;
import ace.oauth2.provider.common.enums.AuthorizationCodeServicesImplEnum;
import ace.oauth2.provider.common.properties.OAuth2Property;
import ace.oauth2.provider.common.support.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.BaseCodec;
import org.redisson.codec.JsonJacksonCodec;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.jackson2.CoreJackson2Module;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.CompositeTokenGranter;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
import org.springframework.security.oauth2.provider.TokenGranter;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeTokenGranter;
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.implicit.ImplicitTokenGranter;
import org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter;
import org.springframework.security.oauth2.provider.refresh.RefreshTokenGranter;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.*;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import org.springframework.security.web.jackson2.WebJackson2Module;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author Caspar
 * @contract 279397942@qq.com
 * @create 2020/1/14 15:57
 * @description 授权服务器配置 默认配置类{@link OAuth2AuthorizationServerConfiguration}
 */
@EnableConfigurationProperties(OAuth2Property.class)
@AutoConfigureAfter(value = {SecurityConfigurerAdapter.class, RedissonConfig.class})
@Configuration
public class OAuth2AuthorizationServerSupportConfig {
    @Autowired
    private OAuth2Property oAuth2Property;
    @Autowired
    private AuthenticationManager authenticationManager;

    @Bean
    @ConditionalOnMissingBean
    public DefaultClientDetailsService defaultClientDetailsService(ClientDetailsConverter clientDetailsConverter,
                                                                   DefaultClientDetails defaultClientDetails) {
        return DefaultClientDetailsService.builder()
                .clientDetailsConverter(clientDetailsConverter)
                .defaultClientDetails(defaultClientDetails)
                .oAuth2Property(oAuth2Property)
                .build();
    }

    /**
     * default oauth2 client
     *
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public DefaultClientDetails baseClientDetails(ClientDetailsConverter clientDetailsConverter) {
        return clientDetailsConverter.toDefaultClientDetailsFrom(oAuth2Property.getDefaultClientProperty());
    }

    /**
     * jwt格式封装token
     *
     * @return
     */
//    @Bean
//    @ConditionalOnMissingBean
//    public JwtAccessTokenConverter jwtAccessTokenConverter() {
//        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
//        // 设置jwt加解密秘钥，不设置会随机一个
//        jwtAccessTokenConverter.setSigningKey(oAuth2Property.getTokenProperty().getJwtSigningKey());
//        return jwtAccessTokenConverter;
//    }

    /**
     * token object extension additional
     *
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public TokenEnhancer tokenEnhancer() {
        TokenEnhancer tokenEnhancer = new DefaultTokenEnhancer(oAuth2Property);
        return tokenEnhancer;
    }

    /**
     * token enhancer chain
     *
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public TokenEnhancerChain tokenEnhancerChain(TokenEnhancer tokenEnhancer) {
        // 采用token转jwt，并添加一些自定义信息（token增强）（有默认非必须）
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(
                Arrays.asList(
                        tokenEnhancer
                )
        );
        return tokenEnhancerChain;
    }

    /**
     * 配置授权码模式授权码服务 {@link AuthorizationCodeServicesImplEnum}
     *
     * @return
     */
    @Primary
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(value = "ace.security.oauth2.authorization-code-services-impl-enum", havingValue = "redis_redisson", matchIfMissing = true)
    public AuthorizationCodeServices redissonAuthorizationCodeServices(RedissonClient redissonClient) {
        return new RedissonAuthorizationCodeServices(redissonClient);
    }

    /**
     * 配置授权码模式授权码服务 {@link AuthorizationCodeServicesImplEnum}
     *
     * @return
     */
    @Primary
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(value = "ace.security.oauth2.authorization-code-services-impl-enum", havingValue = "in_memory")
    public AuthorizationCodeServices inMemoryAuthorizationCodeServices() {
        return new InMemoryAuthorizationCodeServices();
    }

    /**
     * 配置选项{@link ace.oauth2.provider.common.enums.TokenStoreImplEnum}
     *
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(value = "ace.security.oauth2.token-store-impl-enum", havingValue = "redis_redisson", matchIfMissing = true)
    public TokenStore redissonTokenStore(RedissonClient redissonClient) {
        TokenStore tokenStore = new RedissonTokenStore(redissonClient);
        return tokenStore;
    }

    /**
     * 配置选项{@link ace.oauth2.provider.common.enums.TokenStoreImplEnum}
     *
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(value = "ace.security.oauth2.token-store-impl-enum", havingValue = "in_memory")
    public TokenStore tokenStore() {
        TokenStore tokenStore = new InMemoryTokenStore();
        return tokenStore;
    }

    /**
     * 创建grant_type列表
     */
    @Bean
    @ConditionalOnMissingBean
    public TokenGranter tokenGranter(AuthorizationServerTokenServices tokenServices,
                                     AuthorizationCodeServices authorizationCodeServices,
                                     DefaultClientDetailsService defaultClientDetailsService,
                                     OAuth2RequestFactory oAuth2RequestFactory) {
        List<TokenGranter> list = new ArrayList<>();
        // 这里配置密码模式、刷新token模式、自定义手机号验证码模式、授权码模式、简化模式
        list.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices, defaultClientDetailsService, oAuth2RequestFactory));
        list.add(new RefreshTokenGranter(tokenServices, defaultClientDetailsService, oAuth2RequestFactory));
        list.add(new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, defaultClientDetailsService, oAuth2RequestFactory));
        list.add(new ImplicitTokenGranter(tokenServices, defaultClientDetailsService, oAuth2RequestFactory));
        return new CompositeTokenGranter(list);
    }

    @Bean
    @ConditionalOnMissingBean
    public OAuth2RequestFactory oAuth2RequestFactory(DefaultClientDetailsService defaultClientDetailsService) {
        return new DefaultOAuth2RequestFactory(defaultClientDetailsService);
    }

    @Primary
    @Bean
    @ConditionalOnMissingBean
    public AuthorizationServerTokenServices tokenServices(TokenStore tokenStore,
                                                          DefaultClientDetailsService defaultClientDetailsService,
                                                          TokenEnhancerChain tokenEnhancerChain) {
        // 配置TokenServices参数
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setTokenStore(tokenStore);
        // 是否支持刷新Token
        tokenServices.setSupportRefreshToken(oAuth2Property.getTokenProperty().getSupportRefreshToken());
        tokenServices.setReuseRefreshToken(oAuth2Property.getTokenProperty().getReuseRefreshToken());
        tokenServices.setClientDetailsService(defaultClientDetailsService);
        tokenServices.setTokenEnhancer(tokenEnhancerChain);
        // 设置accessToken和refreshToken的默认超时时间(如果clientDetails的为null就取默认的，
        // 如果clientDetails的不为null取clientDetails中的)
        tokenServices.setAccessTokenValiditySeconds(oAuth2Property.getTokenProperty().getAccessTokenValiditySeconds());
        tokenServices.setRefreshTokenValiditySeconds(oAuth2Property.getTokenProperty().getRefreshTokenValiditySeconds());
        tokenServices.setAuthenticationManager(authenticationManager);
        return tokenServices;
    }

    @Bean
    @ConditionalOnMissingBean
    public ClientDetailsConverter clientDetailsConverter() {
        return new ClientDetailsConverterImpl();
    }

}
